

#ifndef __AURA_LOADMODULE_H__
#define __AURA_LOADMODULE_H__

#include <auraplus.h>
#include <linknode.h>
#include <interlocked.h>

#if defined(_android)
#include <sys/mman.h>
#else

#define PAGE_SHIFT_4K               (12)
#define PAGE_SIZE_4K                (1u << PAGE_SHIFT_4K)

#define PAGE_SIZE                   PAGE_SIZE_4K

#define PAGE_MASK_4K                (~(PAGE_SIZE_4K - 1))
#define PAGE_MASK                   PAGE_MASK_4K
#endif

#define MIN_BUFFER_SIZE 0x1000

#define MIN(a, b)               (((a) < (b))? (a) : (b))

#define FALL_INTO(addr, base, size) \
    ((addr) >= (base) && (addr) < (base) + (size))

//
// WinNT compatible dll entry Op values
//
#define DLL_PROCESS_ATTACH 1
#define DLL_PROCESS_DETACH 0

#if 0
#define DLL_THREAD_ATTACH  2
#define DLL_THREAD_DETACH  3
#endif

template <class type>
INLINE type RoundUp2(type n, size_t size)
{
    return (n + size - 1) & ~(size - 1);
}

class Section;
class CModule;


class CLoadedFile {
public:
    CLoadedFile()
    {
        m_iFd = -1;
        m_uFileSize = 0;
        m_pBuffer = NULL;
    }

    ~CLoadedFile();

    Aura_ECode Init(const Aura_PWChar wcsName, CModule * pModule);

    Aura_ECode Read(Aura_UInt32 offset, Aura_UInt32 size, Aura_PVoid *ppBuf,
                 Aura_Bool bUseInterBuf = AURA_TRUE);

    INLINE Aura_UInt32 GetSize() const { return m_uFileSize;}

    INLINE Aura_Int32 GetFd() const { return m_iFd;}

protected:
    Aura_Int32    m_iFd;
    Aura_UInt32   m_uFileSize;
    Aura_PVoid    m_pBuffer;
};

typedef enum ModuleFormatType {
    ModuleFormatType_RESERV,
    ModuleFormatType_PE,
    ModuleFormatType_ELF,
} ModuleFormatType;

typedef enum ModuleTachStatus {
    ModuleTachStatus_INIT,
    ModuleTachStatus_ATTACHED,
    ModuleTachStatus_DETACHED,
} ModuleTachStatus;

typedef enum ModuleFileType {
    ModuleFileType_EXE  = 0,
    ModuleFileType_DLL  = 0x2000, // equal to IMAGE_FILE_DLL (in peimage.h)
    ModuleFileType_MARK = 0x2000
} ModuleFileType;

EXTERN_C Aura_Handle     g_hAttachLock;

class CModule : public DLinkNode {
// member functions
//
public:
    Aura_UInt32 AddRef(void);
    Aura_UInt32 Release(void);

    CModule();
    Aura_Bool AllocMemery(Aura_UInt32 uImageBase, Aura_UInt32 uTotalSize);
    Aura_Bool AttachProcess(Aura_PVoid preserved);
    Aura_Bool DetachProcess(Aura_PVoid preserved);
#if 0
    Aura_Bool AttachThread(Aura_PVoid preserved);
    Aura_Bool DetachThread(Aura_PVoid preserved);
#endif
// member variable
//
public:
    Aura_PWChar         m_wszName;
    Aura_PWChar         m_wszFullPath;

    Aura_UInt32            m_uImageBase;
    Aura_UInt32            m_uTotalSize;
    Aura_UInt32            m_uRelocOffset;
    Aura_UInt32            m_uEntryPoint;

    Aura_UInt32            m_cSections;
    Section *         m_pSections;

    Section *         m_pExport;
    Aura_UInt32            m_uExportOffset;
    Aura_UInt32            m_uExportSize;

    Section *         m_pImport;
    Aura_UInt32            m_uImportOffset;
    Aura_UInt32            m_uImportSize;

    Section *         m_pResource;

    DLinkNode         m_rModuleList;

    ModuleFormatType  m_formatType;

    ModuleFileType    m_fileType;

    ModuleTachStatus  m_attached;
    friend void GUnloadModule(CModule * pModule);
    friend CModule * FindModule(const Aura_PWChar wszName);
protected:
    Interlocked     m_cRef;
};

inline CModule::CModule() :
    m_wszName(NULL),
    m_wszFullPath(NULL),
    m_uImageBase(0),
    m_uTotalSize(0),
    m_pSections(NULL),
    m_pExport(NULL),
    m_pImport(NULL),
    m_pResource(NULL),
    m_attached(ModuleTachStatus_INIT)
{

    m_rModuleList.Initialize();
}

struct ModuleRef : public DLinkNode {
    CModule *        m_pModule;

    ModuleRef(CModule *pModule) : m_pModule(pModule) { }
};


class Section {
public:
    CModule         *m_pOwner;
    char            m_szName[9];

    Aura_UInt32          m_uVAddr;
    Aura_UInt32          m_uVSize;
    Aura_UInt32          m_uProtect;

public:
    void Initialize(CModule * pModule) {
        m_pOwner = pModule;
        m_szName[8] = 0;
        m_uProtect = 0;
    }

    void RelocAddr(Aura_UInt32 vAddr, Aura_UInt32 uOffset, Aura_UInt32 uType);

    void MemZero();

    void MemCopy(void * p, int nSize);

    void MemCopy(Aura_UInt32 uOffset, Aura_UInt32 uSize, void * p);

    void SetAddr(Aura_UInt32 uAddr) {
        assert((uAddr & ~PAGE_MASK) == 0);
        m_uVAddr = uAddr;
    }

    void SetSize(Aura_UInt32 uSize) {
        assert(uSize != 0);
        m_uVSize = RoundUp2(uSize, PAGE_SIZE);
    }

    Aura_ECode MapSection(Aura_Int32 iFd, Aura_UInt32 uFileoffset, Aura_UInt32 size);

};


EXTERN_C Aura_PVoid AnonymousMmap( Aura_PVoid start, Aura_UInt32 size, Aura_Int32 prot, Aura_Int32 flags );
EXTERN_C Aura_Int32 AnonymousMunmap( Aura_PVoid start, Aura_UInt32 size);
EXTERN_C Aura_Int32 GetModuleFullPath(char *pszFullPath, char *pszModuleName);
EXTERN_C Aura_Bool ProcessAttach(CModule *pModule, Aura_PVoid preserved);
EXTERN_C void AddToAttachList(CModule *pModule);

Aura_ECode GLoadModuleA(
        CHAR *szName,
        Aura_UInt32 dwFlags,
        ModuleFileType  modFileType,
        CModule ** ppModule);

#endif // __AURA_LOADMODULE_H__
